package free.order.common.dao;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import free.order.common.Page;

/**
 * Hibernate Dao的泛型基类. <p/> 继承于Spring的<code>HibernateDaoSupport</code>,提供常用的若干便捷查询方法，并对返回值作了泛型类型转换.
 * 
 * @author maoliang
 */
@SuppressWarnings("unchecked")
public class BaseDao<T> extends HibernateDaoSupport
{
    protected Class<T> entityClass;// DAO所管理的Entity类型.

    public BaseDao(Class<T> entityClass)
    {
        this.entityClass = entityClass;
    }

    /**
     * 取得entityClass
     * 
     * @return
     * @author maoliang
     */
    protected Class<T> getEntityClass()
    {
        return entityClass;
    }

    /**
     * 根据ID获取对象. 实际调用Hibernate的session.load()方法返回实体或其proxy对象. 如果对象不存在，抛出异常.
     * 
     * @param id
     * @return
     * @author maoliang
     */
    public T get(Serializable id)
    {
        return (T) getHibernateTemplate().get(entityClass, id);
    }

    /**
     * 获取全部对象.
     * 
     * @return
     * @author maoliang
     */
    public List<T> getAll()
    {
        return getHibernateTemplate().loadAll(entityClass);
    }

    /**
     * 获取全部对象,带排序字段与升降序参数.
     * 
     * @param orderBy
     * @param isAsc
     * @return
     * @author maoliang
     */
    public List<T> getAll(String orderBy, boolean isAsc)
    {
        Assert.hasText(orderBy);
        if (isAsc)
            return getHibernateTemplate().findByCriteria(
                                                         DetachedCriteria.forClass(entityClass)
                                                                 .addOrder(Order.asc(orderBy)));
        else
            return getHibernateTemplate().findByCriteria(
                                                         DetachedCriteria.forClass(entityClass)
                                                                 .addOrder(Order.desc(orderBy)));
    }

    /**
     * 查找相匹配的对象列表
     * 
     * @param o
     * @return
     * @author maoliang
     */
    public List<T> getByExample(Object o)
    {
        List<T> results = getHibernateTemplate().findByExample(o);
        return results;
    }

    /**
     * 保存对象
     * 
     * @param o
     * @author maoliang
     */
    public void save(T o)
    {
        getHibernateTemplate().save(o);
    }

    /**
     * 编辑对象
     * 
     * @param o
     * @author maoliang
     */
    public void update(T o)
    {
        getHibernateTemplate().update(o);
    }

    /**
     * 保存或编辑对象.
     * 
     * @param o
     * @author maoliang
     */
    public void saveOrUpdate(T o)
    {
        getHibernateTemplate().saveOrUpdate(o);
    }

    /**
     *  删除对象
     * 
     * @param o
     * @author maoliang
     */
    public void remove(T o)
    {
        getHibernateTemplate().delete(o);
    }

    /**
     *  根据ID删除对象
     * 
     * @param id
     * @author maoliang
     */
    public void removeById(Serializable id)
    {
        remove(get(id));
    }

    public void flush()
    {
        getHibernateTemplate().flush();
    }

    public void clear()
    {
        getHibernateTemplate().clear();
    }

    /**
     * 创建Query对象.
     * 对于需要first,max,fetchsize,cache,cacheRegion等诸多设置的函数,可以在返回Query后自行设置.
     * 留意可以连续设置,如下：
     * 
     * <pre>
     * dao.getQuery(hql).setMaxResult(100).setCacheable(true).list();
     * </pre>
     * 
     * 调用方式如下：
     * 
     * <pre>
     *        dao.createQuery(hql)
     *        dao.createQuery(hql,arg0);
     *        dao.createQuery(hql,arg0,arg1);
     *        dao.createQuery(hql,new Object[arg0,arg1,arg2])
     * </pre>
     * 
     * @param values
     *            可变参数.
     * @author maoliang
     */
    public Query createQuery(String hql, Object... values)
    {
        Assert.hasText(hql);
        Query query = getSession().createQuery(hql);
        for (int i = 0; i < values.length; i++)
        {
            query.setParameter(i, values[i]);
        }
        return query;
    }

    /**
     * 创建Criteria对象.
     * 
     * @param criterions
     *            可变的Restrictions条件列表,见{@link #createQuery(String,Object...)}
     */
    public Criteria createCriteria(Session session, Criterion... criterions)
    {
        Criteria criteria = session.createCriteria(entityClass);

        for (Criterion c : criterions)
        {
            criteria.add(c);
        }
        return criteria;
    }

    /**
     * 创建Criteria对象，带排序字段与升降序字段.
     * 
     * @see #createCriteria(Class,Criterion[])
     */
    public Criteria createCriteria(Session session, String orderBy, boolean isAsc, Criterion... criterions)
    {
        Assert.hasText(orderBy);

        Criteria criteria = createCriteria(session, criterions);

        if (isAsc)
            criteria.addOrder(Order.asc(orderBy));
        else
            criteria.addOrder(Order.desc(orderBy));

        return criteria;
    }

    /**
     * 根据hql查询,直接使用HibernateTemplate的find函数.
     * 
     * @param values
     *            可变参数,见{@link #createQuery(String,Object...)}
     */
    public List find(String hql, Object... values)
    {
        Assert.hasText(hql);
        return getHibernateTemplate().find(hql, values);
    }

    /**
     * 根据属性名和属性值查询对象.
     * 
     * @return 符合条件的对象列表
     * @throws Exception
     */
    public List<T> findBy(String propertyName, Object value) throws RuntimeException
    {
        Assert.hasText(propertyName);
        Session session = null;
        List<T> ll = new ArrayList<T>();
        try
        {
        	session = getSession();
            ll = createCriteria(session, Restrictions.eq(propertyName, value)).list();
        }
        catch(RuntimeException e)
        {
            throw e;
        }
        finally
        {
            if (session != null)
            {
                this.releaseSession(session);
            }
        }
        return ll;
    }

    //  /**
    //  * 根据属性名和属性值查询对象.
    //  * 
    //  * @return 符合条件的对象列表    //  */
    // public List<T> findBy(String propertyName, Object value)
    // {
    //     Assert.hasText(propertyName);
    //     return createCriteria(Restrictions.eq(propertyName, value)).list();
    // }
    /**
     * 根据属性名和属性值查询对象,带排序参数.
     */
    public List<T> findBy(String propertyName, Object value, String orderBy, boolean isAsc)
    {
        Assert.hasText(propertyName);
        Assert.hasText(orderBy);
        Session session = null;
        List<T> ll = new ArrayList<T>();
        try
        {
        	session = getSession();
            ll = createCriteria(session, orderBy, isAsc, Restrictions.eq(propertyName, value)).list();
        }
        catch(RuntimeException e)
        {
            throw e;
        }
        finally
        {
            if (session != null)
            {
                this.releaseSession(session);
            }
        }
        return ll;
    }

    //    /**
    //     * 根据属性名和属性值查询对象,带排序参数.
    //     */
    //    public List<T> findBy(String propertyName, Object value, String orderBy, boolean isAsc)
    //    {
    //        Assert.hasText(propertyName);
    //        Assert.hasText(orderBy);
    //        return createCriteria(orderBy, isAsc, Restrictions.eq(propertyName, value)).list();
    //    }
    /**
     * 根据属性名和属性值集合查询对象     * 
     * @param propertyName
     * @param values
     * @return
     * @author maoliang
     */
    public List<T> findByValues(String propertyName, Collection values)
    {
        Assert.hasText(propertyName);
        Session session = null;
        List<T> ll = new ArrayList<T>();
        try
        {
        	session = getSession();
            ll = createCriteria(session, Restrictions.in(propertyName, values)).list();
        }
        catch(RuntimeException e)
        {
            throw e;
        }
        finally
        {
            if (session != null)
            {
                this.releaseSession(session);
            }
        }
        return ll;

    }

    //    /**
    //     * 根据属性名和属性值集合查询对象    //     * 
    //     * @param propertyName
    //     * @param values
    //     * @return
    //     * @author maoliang
    //     */
    //    public List<T> findByValues(String propertyName, Collection values)
    //    {
    //        Assert.hasText(propertyName);
    //        return createCriteria(Restrictions.in(propertyName, values)).list();
    //    }

    /**
     * 根据属性名集合与属性值集合查询对象     * 
     * @param propertyName
     * @param values
     * @return
     * @author maoliang
     */
    public List<T> findByNamesAndValues(List<String> propertyNames, List values)
    {
        List<T> ll = new ArrayList<T>();
        Session session = getSession();
        try
        {
            Criteria criteria = session.createCriteria(entityClass);
            for (int i = 0; i < propertyNames.size(); i++)
            {
                String propertyName = propertyNames.get(i);
                Object value = values.get(i);
                if (value != null)
                {
                    criteria.add(Restrictions.eq(propertyName, value));
                }
                else
                {
                    criteria.add(Restrictions.isNull(propertyName));
                }
            }
            ll = criteria.list();
        }
        catch(RuntimeException e)
        {
            throw e;
        }
        finally
        {
            if (session != null)
            {
                releaseSession(session);
            }
        }
        return ll;
    }

    /**
     * 根据属性名和属性值查询唯一对象.
     * 
     * @return 符合条件的唯一对象 or null if not found.
     */
    public T findUniqueBy(String propertyName, Object value)
    {
        Assert.hasText(propertyName);
        Session session = null;
        Object obj = new Object();
        try
        {
        	session = getSession();
            obj = createCriteria(session, Restrictions.eq(propertyName, value)).uniqueResult();
        }
        catch(RuntimeException e)
        {
            throw e;
        }
        finally
        {
            if (session != null)
            {
                this.releaseSession(session);
            }
        }
        return (T) obj;
    }

    //    /**
    //     * 根据属性名和属性值查询唯一对象.
    //     * 
    //     * @return 符合条件的唯一对象 or null if not found.
    //     */
    //    public T findUniqueBy(String propertyName, Object value)
    //    {
    //        Assert.hasText(propertyName);
    //        return (T) createCriteria(Restrictions.eq(propertyName, value)).uniqueResult();
    //    }
    /**
     * 判断对象某些属性的值在数据库中是否唯一.
     * 
     * @param uniquePropertyNames
     *            在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
     */
    public boolean isUnique(Object entity, String uniquePropertyNames)
    {
        Assert.hasText(uniquePropertyNames);
        Session session = null;
        String[] nameList = uniquePropertyNames.split(",");
    	session = getSession();
        Criteria criteria = createCriteria(session).setProjection(Projections.rowCount());
        try
        {
            // 循环加入唯一列
            for (String name : nameList)
            {
                criteria.add(Restrictions.eq(name, PropertyUtils.getProperty(entity, name)));
            }

            // 以下代码为了如果是update的情况,排除entity自身.

            String idName = getIdName(entityClass);

            // 取得entity的主键值
            Serializable id = getId(entity);

            // 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断
            if (id != null)
                criteria.add(Restrictions.not(Restrictions.eq(idName, id)));
        }
        catch(Exception e)
        {
            ReflectionUtils.handleReflectionException(e);
        }
        boolean result = false;
        try
        {
            result = (Integer) criteria.uniqueResult() == 0;
        }
        catch(RuntimeException e)
        {
            throw e;
        }
        finally
        {
            if (session != null)
            {
                this.releaseSession(session);
            }
        }
        return result;
    }

    //    /**
    //     * 判断对象某些属性的值在数据库中是否唯一.
    //     * 
    //     * @param uniquePropertyNames
    //     *            在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
    //     */
    //    public boolean isUnique(Object entity, String uniquePropertyNames)
    //    {
    //        Assert.hasText(uniquePropertyNames);
    //        Criteria criteria = createCriteria().setProjection(Projections.rowCount());
    //        String[] nameList = uniquePropertyNames.split(",");
    //        try
    //        {
    //            // 循环加入唯一列
    //            for (String name : nameList)
    //            {
    //                criteria.add(Restrictions.eq(name, PropertyUtils.getProperty(entity, name)));
    //            }
    //
    //            // 以下代码为了如果是update的情况,排除entity自身.
    //
    //            String idName = getIdName(entityClass);
    //
    //            // 取得entity的主键值
    //            Serializable id = getId(entity);
    //
    //            // 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断
    //            if (id != null)
    //                criteria.add(Restrictions.not(Restrictions.eq(idName, id)));
    //        }
    //        catch(Exception e)
    //        {
    //            ReflectionUtils.handleReflectionException(e);
    //        }
    //        return (Integer) criteria.uniqueResult() == 0;
    //    }

    /**
     * 取得对象的主键值,辅助函数
     * 
     * @param entity
     * @return
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @author maoliang
     */
    public Serializable getId(Object entity) throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException
    {
        Assert.notNull(entity);
        Assert.notNull(entityClass);
        return (Serializable) PropertyUtils.getProperty(entity, getIdName(entityClass));
    }

    /**
     * 取得对象的主键名,辅助函数.
     * 
     * @param clazz
     * @return
     * @author maoliang
     */
    public String getIdName(Class clazz)
    {
        Assert.notNull(clazz);
        ClassMetadata meta = getSessionFactory().getClassMetadata(clazz);
        Assert.notNull(meta, "Class " + clazz + " not define in hibernate session factory.");
        String idName = meta.getIdentifierPropertyName();
        Assert.hasText(idName, clazz.getSimpleName() + " has no identifier property define.");
        return idName;
    }

    /**
     * 消除与 Hibernate Session 的关联
     * @param entity
     * @author maoliang
     */
    public void evit(Object entity)
    {
        getHibernateTemplate().evict(entity);
    }
    
    public Page<T> getAllByPage(Page<T> page){
    	Assert.notNull(page);
    	Criteria criteria = this.getSession().createCriteria(entityClass);
    	criteria.setFirstResult(page.getPageNum()*page.getPageSize());
    	criteria.setMaxResults(page.getPageSize());
    	page.setItems(criteria.list());
    	return page;
    	
    }

}